using System;
using System.Collections;
using System.Text;

namespace RandomRat {

	public delegate void ProgressEventHandler(Object sender, ProgressEventArgs e);

	public class ProgressEventArgs : EventArgs {
		public Process CurProcess;
		public int WorkCompleted;
		public SetArguments SetArgs;
		
		public ProgressEventArgs(Process curProcess, int workCompleted, SetArguments setArgs) {
			CurProcess = curProcess;
			WorkCompleted = workCompleted;
			SetArgs = setArgs;
		}
	}

	public enum Process {SetGeneration, SuccessionSwapping, PatternSwapping, CleanUp,
						ViolationGroupingIndexesExhausted, SetTypeIndexesExhausted, SuccessionCorrection, PatternCorrection}


	/// <summary>
	/// Class for generating sets of random types fitting specified constraints
	/// </summary>
	public class RandomSetGenerator {
		Random rnd;
		
		public event ProgressEventHandler SetGeneration;
		public event ProgressEventHandler SuccessionSwapping;
		public event ProgressEventHandler PatternSwapping;
		public event ProgressEventHandler CleanUp;
		public event ProgressEventHandler ViolationGroupingIndexesExhausted;
		public event ProgressEventHandler SetTypeIndexesExhausted;
		
		public RandomSetGenerator() {
			rnd = new Random(~unchecked((int)DateTime.Now.Ticks));
		}


		#region EVENTS
		
		protected virtual void OnSetGeneration(ProgressEventArgs e) {
			if (SetGeneration != null) {
				SetGeneration(this, e);
			}
		}
		protected virtual void OnSuccessionSwapping(ProgressEventArgs e) {
			if (SuccessionSwapping != null) {
				SuccessionSwapping(this, e);
			}
		}
		protected virtual void OnPatternSwapping(ProgressEventArgs e) {
			if (PatternSwapping != null) {
				PatternSwapping(this, e);
			}
		}
		protected virtual void OnCleanUp(ProgressEventArgs e) {
			if (CleanUp != null) {
				CleanUp(this, e);
			}
		}

		protected virtual void OnViolationGroupingIndexesExhausted(ProgressEventArgs e) {
			if (ViolationGroupingIndexesExhausted != null) {
				ViolationGroupingIndexesExhausted(this, e);
			}
		}
		protected virtual void OnSetTypeIndexesExhausted(ProgressEventArgs e) {
			if (SetTypeIndexesExhausted != null) {
				SetTypeIndexesExhausted(this, e);
			}
		}

		#endregion

	
		#region LIST GENERATION

		// Returns valid list
		public string[] GetValidList(SetArguments setArgs) {

			string[] rndSet = null;

			rndSet = getRawList(setArgs);

			int listsGenerated = 0;
			OnCleanUp(new ProgressEventArgs(Process.CleanUp, listsGenerated, setArgs));

			bool validList = false;
			while (!validList) {
								
				validList = true;
				
				// Creates list meating successionTol
				if (setArgs.SuccessionTol != -1) {
					fixSuccessions(rndSet, setArgs);
				}

				// Fix patter intol
				if (setArgs.patternRules != null && setArgs.patternRules.Length > 0) {
					fixPatterns(rndSet, setArgs);
					validList = inPatternTol(rndSet, setArgs);
				}

				
				if (validList && setArgs.SuccessionTol != -1) {
					validList = inSuccessionTol(rndSet, setArgs);
				}


				listsGenerated++;
				// Show we are working:
				OnCleanUp(new ProgressEventArgs(Process.CleanUp, listsGenerated, setArgs));
			}

			return rndSet;
		}

		
		// Generates raw list (no rulls inforeced)
		private string[] getRawList(SetArguments setArgs) {
			int slots = setArgs.Slots;
			string[] types = setArgs.Types;

			string[] rndSet = new string[slots];

			if (setArgs.forceEqualTypes) {

				if (slots % types.Length != 0) {
					throw new Exception("slots par must be divisible by the number of types");
				}

				int slotsFinished = 0;
			
				int occurrences = rndSet.Length / types.Length;  // nuber of occurences for each type (equaocurence)
				int slotsFree = rndSet.Length; // decremented as slots are filled

				// Create an ArrayList of valid indexes
				ArrayList validIndexes = new ArrayList();
				int indx;
				for (indx = 0; indx < rndSet.Length; indx++) {
					validIndexes.Add(indx);
				}

				int t;
				for (t = 0; t < types.Length; t++) {
					if (t >= types.Length - 1) {
						// All other types have been filled, any null slots left can now be filled
						// with the remaing type
						int i;
						for (i = 0; i < rndSet.Length; i++) {
							if (rndSet[i] == null) {
								rndSet[i] = types[t];
								// SHULD BE EVENT
								slotsFinished++;
								//if (slotsFinished % 5 == 0 || slotsFinished == slots) 
								//textBox4.Text = slotsFinished.ToString();
								OnSetGeneration(new ProgressEventArgs(Process.SetGeneration, slotsFinished, setArgs));
							}
						}
					} else {
						int o;
						for (o = 0; o < occurrences; o++) {
							int fSlot = rnd.Next(0, slotsFree);
							rndSet[(int)validIndexes[fSlot]] = types[t];
							validIndexes.RemoveAt(fSlot);
							slotsFree--;

							// SHULD BE EVENT
							slotsFinished++;
							//if (slotsFinished % 5 == 0 || slotsFinished == slots) 
							//textBox4.Text = slotsFinished.ToString();
							OnSetGeneration(new ProgressEventArgs(Process.SetGeneration, slotsFinished, setArgs));
						}
					}
				}
			
			} else { // equal types not enfocred
				for (int i = 0; i < rndSet.Length; i++) {
					rndSet[i] = types[rnd.Next(0, types.Length)];
				}
			}

			return rndSet;
		}


		#endregion


		#region SUCESSION
		
		// Checks list for sucsession rulles
		private bool inSuccessionTol(string[] rndSet, SetArguments setArgs) {
			return getSuccessionIndx(rndSet, setArgs) == -1;
		}

		
		// Gets starting index of sucsession violation
		private int getSuccessionIndx(string[] rndSet, SetArguments setArgs) {
			int successionTol = setArgs.SuccessionTol;
			
			int indx = 0;
			// Check succession tolerance
			string lastType = null;
			int consecutiveCnt = 1;
			foreach (string type in rndSet) {
				if (lastType == null) {
					lastType = type;
				} else if (type == lastType) {
					consecutiveCnt++;
					if (consecutiveCnt > successionTol) {
						return indx - successionTol;
					}
				} else {
					lastType = type;
					consecutiveCnt = 1;
				}
				indx++;
			}

			return -1;
		}


		// Fixes succeshion intolrence via swaping
		private string[] fixSuccessions(string[] rndSet, SetArguments setArgs) {
			string[] types = setArgs.Types;
			int successionTol = setArgs.SuccessionTol;
			
			// See if user wants an every other list!
			// And give it to them if so
			if (successionTol == 1 && types.Length == 2) {
				int typeIndx = rnd.Next(0, 2);
				
				string type1;
				string type2;
				if (typeIndx == 0) {
					type1 = types[0];
					type2 = types[1];
				} else {
					type1 = types[1];
					type2 = types[0];
				}
				
				int i;
				for (i = 0; i < rndSet.Length; i++) {
					if (i % 2 == 0) {
						rndSet[i] = type1;
					} else {
						rndSet[i] = type2;
					}
				}

				return rndSet;
			}
			
			int swapingCnt = 0;
			int violationGroupingIndexesExhaustedCnt = 0;
			int setTypeIndexesExhaustedCnt = 0;

			int successionIndx;
			
			// Do prosseses until set conforms succesion tol
			bool flag = true;
			while (flag) {
				successionIndx = getSuccessionIndx(rndSet, setArgs);
				if (successionIndx != -1) {
					
					// Create int array of indexs that define the grouping + 1
					// This will represent the index we know for sure point to the same types
					int groupIdxStart = successionIndx;
					int groupIdxStop = successionIndx + successionTol; 
					// INDX chek, should not need, unless breating room added
					//if (groupIdxStop >= rndSet.Length) groupIdxStop = rndSet.Length - 1;
					
					int[] groupingIndexes = new int[groupIdxStop - groupIdxStart + 1];
					int i;
					for (i = 0; i < groupingIndexes.Length; i++) {
						groupingIndexes[i] = i + groupIdxStart;
					}

					// Create an ArrayList of exscluded indexes
					ArrayList groupingIdxExcluded = new ArrayList(successionTol + 1);

					// Create an ArrayList of exscluded types
					ArrayList typeIdxExcluded = new ArrayList(types.Length);

					//-----------------------------------------------------------------
					// 
					//
					string type = null;	 
					int indx1 = -1;		 // Indx of one of the tpyes in the sucsessive grouping
					// that will be swaped with indx2 (determend later)
					int indx1Coded = -1; // represents the array indx of the subset groupingIndexes[]
					// that holds indx1.  ie: indx1 == groupingIndexes[indx1Coded]

					int tottalRepeates = 0;
					bool needToRepeate = true;
					while (needToRepeate) {

						// Get rand int from start to finish indxs which define the grouping
						if (indx1 != -1) {
							// Exclude last indx
							groupingIdxExcluded.Add(indx1Coded);
						}

						indx1Coded = getRndIndx(groupingIndexes, groupingIdxExcluded);/////
						indx1 = groupingIndexes[indx1Coded];

						// Chose a random type excluding type of indx1 above	
						typeIdxExcluded.Clear();
						typeIdxExcluded.Add(getTypeIndx(types, rndSet[indx1]));
						type = types[getRndIndx(types, typeIdxExcluded)];
					
						//Check to see if new type would invalidate sucseshion rules
						bool tryType = true;
						while (tryType) {
							// Test subset
							//SetArguments setArgs = new SetArguments(rndSet.Length, types, successionTol, 9999);

							//if (inSuccessionTol(subSet, successionTol)) {
							if (checkReplacement(rndSet, indx1, type, setArgs)) {
								needToRepeate = false;
								tryType = false;
							} else {
								tottalRepeates++;
								// Try another type
								typeIdxExcluded.Add(getTypeIndx(types, type));
								if (typeIdxExcluded.Count < types.Length) {
									type = types[getRndIndx(types, typeIdxExcluded)];
								} else {
									// All types are eghsosted,
									// let outer loop get anothe indx
									tryType = false;
								}
							}

						}
						if (groupingIdxExcluded.Count + 1 >= groupingIndexes.Length) {
							// All indexs have been tried and the algorythm cant cope
							// ie: all swappings will cause intolorences
							// Swap whatever the curent indx1 is and let it get sorted out later
							OnViolationGroupingIndexesExhausted(
								new ProgressEventArgs(Process.ViolationGroupingIndexesExhausted, 
													  ++violationGroupingIndexesExhaustedCnt, setArgs));
							needToRepeate = false;
						}

					}

					if (setArgs.forceEqualTypes) {

						// Second step: find an indx of the type found above that can be swaped
						// with the first indx (indx1) without creating a sucsession tolrence violation

						// Create int array of all posible seconday swap indexs
						int validSlotsInList = rndSet.Length - rndSet.Length / types.Length;
						int[] secondaryIndexes = new int[validSlotsInList];
						int secIndx = 0;
						for (i = 0; i < rndSet.Length; i++) {
							if (rndSet[i] != rndSet[indx1]) {
								secondaryIndexes[secIndx++] = i;
							}
						}

						// Create an ArrayList of exscluded indexes
						ArrayList secondaryIdxExcluded = new ArrayList();

						int indx2 = -1;			// rndSet index for swaping
						int indx2Coded = -1;	// see indx1Coded above

						tottalRepeates = 0;
						needToRepeate = true;
						while (needToRepeate) {
							// Get rand index
							if (indx2 != -1) {
								// Exclude last indx
								secondaryIdxExcluded.Add(indx2Coded);
							}

							indx2Coded = getRndIndx(secondaryIndexes, secondaryIdxExcluded);
							indx2 = secondaryIndexes[indx2Coded];

							// Test replacment
							//SetArguments setArgs = new SetArguments(rndSet.Length, types, successionTol, 9999);
							//if (inSuccessionTol(subSet, successionTol)) {
							if (checkReplacement(rndSet, indx2, rndSet[indx1], indx1, type, false, setArgs)) {
								needToRepeate = false;
							} else {
								tottalRepeates++;
							}

							if (secondaryIdxExcluded.Count + 1 >= secondaryIndexes.Length) {
								// All the indxs have been tried and all produce erros
								// go with curent indx and let it get worked out later
								OnSetTypeIndexesExhausted(
									new ProgressEventArgs(Process.SetTypeIndexesExhausted, 
									++setTypeIndexesExhaustedCnt, setArgs));
							
								needToRepeate = false;
							}
						}

						// Swap indexes
						string tempType = rndSet[indx1];
						rndSet[indx1] = rndSet[indx2];
						rndSet[indx2] = tempType;

						OnSuccessionSwapping(new ProgressEventArgs(Process.SuccessionSwapping, ++swapingCnt, setArgs));
					
					} else { // equal types not enforced
						// Change violation to new type
						rndSet[indx1] = type;

						OnSuccessionSwapping(new ProgressEventArgs(Process.SuccessionCorrection, ++swapingCnt, setArgs));
					}
				
				} else {
					flag = false;
				}
			}

			
			return rndSet;
		}

	
		#endregion

		
		#region PATTERN

		// Checks list for pattern rulles
		private bool inPatternTol(string[] rndSet, SetArguments setArgs) {
			return getPatternIndx(rndSet, setArgs) == -1;
		}

	
		// Gets starting index of a pattern violation
		private int getPatternIndx(string[] rndSet, SetArguments setArgs) {
			int[][] patternRules = setArgs.patternRules;
			
			int rule;
			for (rule = 0; rule < patternRules.Length; rule++) {
				int group = patternRules[rule][0];
				int tolerance = patternRules[rule][1];

				if (rndSet.Length < group * (tolerance + 1) || tolerance < 1) {
					continue;
				}


				if (group <= 1) {
					throw new Exception("Group param must be greater than 1");
				}
				if (tolerance < 1) {
					throw new Exception("Tolerance param must be greater than 0");
				}
				if (rndSet.Length < 4) {
					throw new Exception("The number of types passed must 4 or more.");
				}
				if (group > rndSet.Length) {
					throw new Exception("The group size cannot exceed the number of types passed.");
				}
			
				string[] testPat = new string[group];

				int i = 0;
				bool flag = true;
				while (flag) {
					// Fill test patern
					bool consecTypes = true;
					int j;
					for (j = 0; j < testPat.Length; j++) {
						testPat[j] = rndSet[i + j];
						if (j != 0 && consecTypes && testPat[j] != testPat[j - 1]) {
							consecTypes = false;
						}
					}

					// procede if pattern is not simply made up of the same types
					if (!consecTypes) {

						int occurrences = 1;
						bool repeat = true;
						while (repeat) {
							// Check next grouping type by type
							int k = 0;
							bool failed = false;
							while (!failed && k < group) {
								if (rndSet[i + k + (group * occurrences)] != testPat[k]) {
									failed = true;
								}
								k++;
							}


							// check if next grouping matched patern
							if (!failed) {
								occurrences++;
								if (occurrences > tolerance) {
									// return starting index of patter violation
									return i;
								}

								if ( (i + group - 1) + (group * occurrences) >= rndSet.Length) {
									repeat = false;
								}

							} else {
								repeat = false;
							}
						}
					}

					i++;
					if ((i + group - 1) + (group * 1) >= rndSet.Length) {
						flag = false;
					}
				}
			}
			
			return -1;
		}


		// Atempts to fix patern intolorens
		private string[] fixPatterns(string[] rndSet, SetArguments setArgs) {
			string[] types = setArgs.Types;
			int swapingCnt = 0;
			
			int violationGroupingIndexesExhaustedCnt = 0;
			int setTypeIndexesExhaustedCnt = 0;

			// Atempt to fix pattern intolrences
			
			int rule;
			for (rule = 0; rule < setArgs.patternRules.Length; rule++) {
			
				int group = setArgs.patternRules[rule][0];
				int tolerance = setArgs.patternRules[rule][1];
			
				bool patternIntolExists = true;
				while (patternIntolExists) {
					int patternIndx = getPatternIndx(rndSet, setArgs);
					if (patternIndx != -1) {
					
						// Randomly select index1 within pattern intol range
						int groupIdxStart = patternIndx;
						int groupIdxStop = patternIndx + (tolerance + 1) * group - 1;
						if (groupIdxStart < 0) groupIdxStart = 0;
						if (groupIdxStop >= rndSet.Length) groupIdxStop = rndSet.Length - 1;


						int[] groupingIndexes = new int[groupIdxStop - groupIdxStart + 1];
						int i;
						for (i = 0; i < groupingIndexes.Length; i++) {
							groupingIndexes[i] = i + groupIdxStart;
						}

						// Create an ArrayList of exscluded indexes
						ArrayList groupingIdxExcluded = new ArrayList();

						// Create an ArrayList of exscluded types
						ArrayList typeIdxExcluded = new ArrayList();

						string type = null;	 
						int indx1 = -1;		 // Indx of one of the tpyes in the sucsessive grouping
						// that will be swaped with indx2 (determend later)
						int indx1Coded = -1; // represents the array indx of the subset groupingIndexes[]
						// that holds indx1.  ie: indx1 == groupingIndexes[indx1Coded]

						int tottalRepeates = 0;
						bool needToRepeate = true;
						while (needToRepeate) {

							// Get rand int from start to finish indxs which define the grouping
							if (indx1 != -1) {
								// Exclude last indx
								groupingIdxExcluded.Add(indx1Coded);
							}


							indx1Coded = getRndIndx(groupingIndexes, groupingIdxExcluded);
							indx1 = groupingIndexes[indx1Coded];
				
							// Chose a random type excluding type of indx1 above	
							typeIdxExcluded.Clear();
							typeIdxExcluded.Add(getTypeIndx(setArgs.Types, rndSet[indx1]));
							type = types[getRndIndx(setArgs.Types, typeIdxExcluded)];
					
							//Check to see if new type would invalidate rules
							bool tryType = true;
							while (tryType) {

							
								if (checkReplacement(rndSet, indx1, type, setArgs)) {
									needToRepeate = false;
									tryType = false;
								} else {
									tottalRepeates++;
									// Try another type
									typeIdxExcluded.Add(getTypeIndx(setArgs.Types, type));
									if (typeIdxExcluded.Count < setArgs.Types.Length) {
										type = types[getRndIndx(setArgs.Types, typeIdxExcluded)];
									} else {
										// All types are eghsosted,
										// let outer loop get anothe indx
										tryType = false;
									}
								}
							}

							// TODO: Check if all indxes have been tried and if so, remidy it.
							if (groupingIdxExcluded.Count + 1 >= groupingIndexes.Length) {
								// All indx have been exgsosted!!!
								// Pretend like everything went fine (and hope for the best)
								OnViolationGroupingIndexesExhausted(
									new ProgressEventArgs(Process.ViolationGroupingIndexesExhausted, 
									++violationGroupingIndexesExhaustedCnt, setArgs));
								needToRepeate = false;
							}
						}

						if (setArgs.forceEqualTypes) {
					
							// Second step: find an indx of the type found above that can be swaped
							// with the first indx (indx1) without creating a sucsession tolrence violation

							// Create int array of all posible seconday swap indexs
							int validSlotsInList = rndSet.Length - rndSet.Length / types.Length;
							int[] secondaryIndexes = new int[validSlotsInList];
							int secIndx = 0;
							for (i = 0; i < rndSet.Length; i++) {
								if (rndSet[i] != rndSet[indx1]) {
									secondaryIndexes[secIndx++] = i;
								}
							}

							// Create an ArrayList of exscluded indexes
							ArrayList secondaryIdxExcluded = new ArrayList();

							int indx2 = -1;			// rndSet index for swaping
							int indx2Coded = -1;	// see indx1Coded above

							tottalRepeates = 0;
							needToRepeate = true;
							while (needToRepeate) {
								// Get rand index
								if (indx2 != -1) {
									// Exclude last indx
									secondaryIdxExcluded.Add(indx2Coded);
								}

								indx2Coded = getRndIndx(secondaryIndexes, secondaryIdxExcluded);

								indx2 = secondaryIndexes[indx2Coded];

								// Check if valid replacement
								if (checkReplacement(rndSet, indx2, rndSet[indx1], indx1, type, false, setArgs)) {
									needToRepeate = false;
								} else {
									tottalRepeates++;
								}

								if (secondaryIdxExcluded.Count + 1 >= secondaryIndexes.Length) {
									// All indx have been exgsosted!!!
									// Pretend like everything went fine (and hope for the best)
									OnSetTypeIndexesExhausted(
										new ProgressEventArgs(Process.SetTypeIndexesExhausted, 
										++setTypeIndexesExhaustedCnt, setArgs));
									needToRepeate = false;
								}

	
							}

							//Console.WriteLine("Tottal Repeates {0}", tottalRepeates.ToString());

							// Do actual swap
							string tempType = rndSet[indx1];
							rndSet[indx1] = rndSet[indx2];
							rndSet[indx2] = tempType;

							OnPatternSwapping(new ProgressEventArgs(Process.PatternSwapping, ++swapingCnt, setArgs));

						} else { // equal types not enforced
							// Change violation to new type
							rndSet[indx1] = type;

							OnPatternSwapping(new ProgressEventArgs(Process.PatternCorrection, ++swapingCnt, setArgs));
						}
					} else {
						patternIntolExists = false;
					}
				}

			}

			return rndSet;

		}


		#endregion



		// Check replacement (Key function in swaping algoritums)
		private bool checkReplacement(string[] rndSet, int indx1, string typeReplacement1, 
			int indx2, string typeReplacement2, bool ignoreIndx2, SetArguments setArgs) {
		
			if (setArgs.SuccessionTol > 0) {

				// Chekc sucsseshion tolrence
				int startIndx = indx1 - setArgs.SuccessionTol;
				int stopIndx = indx1 + setArgs.SuccessionTol;
				if (startIndx < 0) startIndx = 0;
				if (stopIndx >= rndSet.Length) stopIndx = rndSet.Length - 1;
			
				string typeBeingChked = "";
				int succession = 0;
				int i;
				for (i = startIndx; i <= stopIndx; i++) {
					string curType;
					// Chekc to see if this is the index to replace
					if (i == indx1) {
						curType = typeReplacement1;
					} else if (!ignoreIndx2 && i == indx2) {
						curType = typeReplacement2;
					} else {
						curType = rndSet[i];
					}
				
					// Test actual or replaced type
					if (i == startIndx) {
						typeBeingChked = curType;
						succession = 1;
					} else if (curType == typeBeingChked) {
						succession++;
						if (succession > setArgs.SuccessionTol) {
							return false;  // RETURN: NOT IN SUCCESSION TOL!!!
						}
					} else {
						typeBeingChked = curType;			
						succession = 1;
					}
				}
			}

			// Check everyOther tolrence
			// START ITERATOR FOR EACH group and tolorence	

			if (setArgs.patternRules == null || setArgs.patternRules.Length == 0) {
				return true;
			}

			int rule;
			for (rule = 0; rule < setArgs.patternRules.Length; rule++) {

				int group = setArgs.patternRules[rule][0];
				int tolerance = setArgs.patternRules[rule][1];

				if (tolerance > 0 && rndSet.Length >= group * tolerance) {

					int startIndx = indx1 - (tolerance + 1) * group + 1;
					int stopIndx = indx1 + (tolerance + 1) * group - 1;
					if (startIndx < 0) startIndx = 0;
					if (stopIndx >= rndSet.Length) stopIndx = rndSet.Length - 1;

					string[] testPat = new string[group];

					int i = startIndx; // Iterates over every index from startIndx to stopIndx inclusive
					bool flag = true; // Flag to indicate whether to continue or not
					while (flag) {
						// Fill test patern
						bool consecTypes = true;
						int j;
						for (j = 0; j < testPat.Length; j++) {
							string curType;
							// Chekc to see if this is the index to replace
							if (i + j == indx1) {
								curType = typeReplacement1;
							} else if (!ignoreIndx2 && i + j == indx2) {
								curType = typeReplacement2;
							} else {
								curType = rndSet[i + j];
							}
					
							testPat[j] = curType;
							if (j != 0 && consecTypes && testPat[j] != testPat[j - 1]) {
								consecTypes = false;
							}
						}

						// procede if pattern is not simply made up of the same types
						if (!consecTypes) {
							int occurrences = 1;
							bool repeat = true;
							
							//    (first group)  +   (groups after it)
							if ( (i + group - 1) + (group * occurrences) > stopIndx) {
								repeat = false;
							}
							
							while (repeat) {
								// Check next grouping type by type
								int k = 0;
								bool failed = false;
								while (!failed && k < group) {
									string curType;
									// Chekc to see if this is the index to replace
									if (i + k + (group * occurrences) == indx1) {
										curType = typeReplacement1;
									} else if (!ignoreIndx2 && i + k + (group * occurrences) == indx2) {
										curType = typeReplacement2;
									} else {
										curType = rndSet[i + k + (group * occurrences)];
									}

									if (curType != testPat[k]) {
										failed = true;
									}
									k++;
								}


								if (!failed) {
									occurrences++;
									if (occurrences > tolerance) {
										return false; // RETURN: NOT IN PATTERN TOL!!!
									}

									if ( (i + group - 1) + (group * occurrences) > stopIndx) {
										repeat = false;
									}

								} else {
									repeat = false;
								}
							}
						}

						i++;
						if ((i + group - 1) + (group * 1) > stopIndx) {
							flag = false;
						}

					}
				}
			}
			
			return true;
		}

		private bool checkReplacement(string[] rndSet, int indx1, string typeReplacement1, SetArguments setArgs) {
			return checkReplacement(rndSet, indx1, typeReplacement1, -1, "", true, setArgs);
		}


		#region HELPERS
		
		// Helpers for fixSuccessions/fixPatterns functions
		private int getTypeIndx(string[] types, string type) {
			int i;
			for (i = 0; i < types.Length; i++) {
				if (types[i] == type) {
					return i;
				}
			}

			return -1;
		}

		// ReWriten function (has not been tested; not curently used)
		private int getRandomIndex(int[] listOfIndexes, ArrayList excludedIndexes) {
			int indx = -1;

			// If there are no excluded indexes
			if (!(excludedIndexes.Count > 0)) {
				indx = listOfIndexes[rnd.Next(0, listOfIndexes.Length)];

			} else if (excludedIndexes.Count >= listOfIndexes.Length){
				throw new Exception("Exclushion list is as big (or bigger) then the list of indexes!");

			} else {
				// Choose the nth occurence of a valid index
				int nThValidIndx = rnd.Next(0, listOfIndexes.Length - excludedIndexes.Count);
		
				// Iterate listOfIndexes, counting each valid indx untill the nThValidInsx is reached
				int allI = -1;	// Iterates listOfIndxes
				int validI = -1; // Counts nTh valid indx
				while (validI < nThValidIndx && allI < listOfIndexes.Length) {
					allI++;
					if (!excludedIndexes.Contains(excludedIndexes[allI])) validI++;
				}

				indx = listOfIndexes[allI];
			}

			return indx;
		}
		
		private int getRndIndx(int[] list, ArrayList excludedIndxs) {
			if (list.Length <= excludedIndxs.Count) {
				throw new Exception("Exclushion list is as big as list!");
			}
			
			// Chose a random type from the types which are not excluded	
			int typeSlot = rnd.Next(0, list.Length - excludedIndxs.Count); // "type spesifc index" we want
			int nThValidType = 0; // "type spesific index" iterator for types
					
			int indx = 0; // array index for types
			bool flag2 = true;
			while (flag2) {
				int excludedIndx = 0;
				bool typeExcluded = false;
				while (!typeExcluded && excludedIndx < excludedIndxs.Count) {
					if ((int)excludedIndxs[excludedIndx++] == indx) {
						typeExcluded = true;
					}
				}

				if (!typeExcluded) {
					if (nThValidType >= typeSlot) {
						// nThValidTYype reached, return array index
						return indx;
					} else {
						nThValidType++;
					}
				}
				indx++;
			}

			// Should never reach
			return -1;
		}

		private int getRndIndx(object[] list, ArrayList excludedIndxs) {
			// Chose a random type from the types which are not excluded	
			int typeSlot = rnd.Next(0, list.Length - excludedIndxs.Count); // "type spesifc index" we want
			int nThValidType = 0; // "type spesific index" iterator for types
					
			int indx = 0; // array index for types
			bool flag2 = true;
			while (flag2) {
				int excludedIndx = 0;
				bool typeExcluded = false;
				while (!typeExcluded && excludedIndx < excludedIndxs.Count) {
					if ((int)excludedIndxs[excludedIndx++] == indx) {
						typeExcluded = true;
					}
				}

				if (!typeExcluded) {
					if (nThValidType >= typeSlot) {
						// nThValidTYype reached, return array index
						return indx;
					} else {
						nThValidType++;
					}
				}
				indx++;
			}

			// Should never reach
			return -1;
		}


		#endregion




	
	}

}
